﻿// -------------------------------------------------------------------------------------------------------------------------------
// <copyright file="PropertyHasUnexpectedTypeException.cs" company="Exul">
//     Copyright (c) Exul. All rights reserved.
// </copyright>
// <author>
//     Exul
// </author>
// -------------------------------------------------------------------------------------------------------------------------------

namespace ExulLibrary.Code.Analysis.StyleCop.Rules.Exceptions
{
    #region Usings

    using System;
    using System.Globalization;
    using System.Runtime.Serialization;
    using System.Security.Permissions;

    using ExulLibrary.Code.Analysis.StyleCop.Rules.Properties;

    #endregion

    /// <summary>
    ///     Describes an error when property has unexpected type.
    /// </summary>
    [Serializable]
    public sealed class PropertyHasUnexpectedTypeException : ArgumentException
    {
        #region Constructors and Destructors

        #region public

        /// <summary>
        ///     Initializes a new instance of the <see cref="PropertyHasUnexpectedTypeException"/> class.
        /// </summary>
        /// <param name="paramName">
        ///     The parameter name.
        /// </param>
        /// <param name="propertyName">
        ///     The property name.
        /// </param>
        /// <param name="actualPropertyType">
        ///     The actual type of property.
        /// </param>
        /// <param name="expectedPropertyType">
        ///     The expected type of property.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="actualPropertyType"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="expectedPropertyType"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        public PropertyHasUnexpectedTypeException(
            string paramName, string propertyName, Type actualPropertyType, Type expectedPropertyType)
            : this(paramName, propertyName, actualPropertyType, expectedPropertyType, null)
        {
        }

        /// <summary>
        ///     Initializes a new instance of the <see cref="PropertyHasUnexpectedTypeException"/> class.
        /// </summary>
        /// <param name="paramName">
        ///     The parameter name.
        /// </param>
        /// <param name="propertyName">
        ///     The property name.
        /// </param>
        /// <param name="actualPropertyType">
        ///     The actual type of property.
        /// </param>
        /// <param name="expectedPropertyType">
        ///     The expected type of property.
        /// </param>
        /// <param name="innerException">
        ///     The inner exception.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="actualPropertyType"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="expectedPropertyType"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        public PropertyHasUnexpectedTypeException(
            string paramName, string propertyName, Type actualPropertyType, Type expectedPropertyType, Exception innerException)
            : base(Resources.PropertyHasUnexpectedType, paramName, innerException)
        {
            if (actualPropertyType == null)
            {
                throw new ArgumentNullException("actualPropertyType");
            }

            if (expectedPropertyType == null)
            {
                throw new ArgumentNullException("expectedPropertyType");
            }

            this.PropertyName = propertyName;
            this.ActualPropertyType = actualPropertyType;
            this.ExpectedPropertyType = expectedPropertyType;
        }

        #endregion

        #region private

        /// <summary>
        ///     Initializes a new instance of the <see cref="PropertyHasUnexpectedTypeException"/> class.
        /// </summary>
        /// <param name="info">
        ///     The object that holds the serialized object data.
        /// </param>
        /// <param name="context">
        ///     The contextual information about the source or destination.
        /// </param>
        private PropertyHasUnexpectedTypeException(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
            this.PropertyName = info.GetString("PropertyName");
            this.ActualPropertyType = (Type)info.GetValue("ActualPropertyType", typeof(Type));
            this.ExpectedPropertyType = (Type)info.GetValue("ExpectedPropertyType", typeof(Type));
        }

        #endregion

        #endregion

        #region Properties

        #region public

        /// <summary>
        ///     Gets the actual type of property.
        /// </summary>
        /// <value>
        ///     The actual type of property.
        /// </value>
        public Type ActualPropertyType
        {
            get;
            private set;
        }

        /// <summary>
        ///     Gets the expected type of property.
        /// </summary>
        /// <value>
        ///     The expected type of property.
        /// </value>
        public Type ExpectedPropertyType
        {
            get;
            private set;
        }

        /// <summary>
        ///     Gets the name of property.
        /// </summary>
        /// <value>
        ///     The name of property.
        /// </value>
        public string PropertyName
        {
            get;
            private set;
        }

        #endregion

        #endregion

        #region Overridden Properties

        #region public

        /// <summary>
        ///     Gets the error message and the parameter name, or only the error message if no parameter name is set.
        /// </summary>
        /// <value>
        ///     A text string describing the details of the exception.
        /// </value>
        public override string Message
        {
            get
            {
                return string.Format(
                    CultureInfo.CurrentCulture,
                    base.Message,
                    this.ParamName,
                    this.PropertyName,
                    this.ExpectedPropertyType.FullName,
                    this.ActualPropertyType.FullName);
            }
        }

        #endregion

        #endregion

        #region Overridden Methods

        #region public

        /// <summary>
        ///     Sets the <see cref="SerializationInfo"/> object with the parameter name and additional exception information.
        /// </summary>
        /// <param name="info">
        ///     The object that holds the serialized object data.
        /// </param>
        /// <param name="context">
        ///     The contextual information about the source or destination.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="info"/> is <see langword="null"/>.
        /// </exception>
        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);

            info.AddValue("PropertyName", this.PropertyName);
            info.AddValue("ActualPropertyType", this.ActualPropertyType);
            info.AddValue("ExpectedPropertyType", this.ExpectedPropertyType);
        }

        #endregion

        #endregion
    }
}